package top.haijunit.work.configurer;

import com.baomidou.lock.exception.LockException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.method.MethodValidationException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import top.haijunit.work.core.doamin.ExceptionEnum;
import top.haijunit.work.core.doamin.Result;
import top.haijunit.work.utils.SpringWebUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 统一异常处理
 * 处理全局异常，还可以实现接口 {@link org.springframework.web.servlet.HandlerExceptionResolver}
 *
 * @author zhanghaijun
 * @since 2024-08-07
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {

    @ExceptionHandler(value = Throwable.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<String> exceptionHandler(Throwable throwable) {
        log.error("全局异常捕获：{}", throwable.getClass().getName(), throwable);
        return Result.of(ExceptionEnum.INTERNAL_SERVER_ERROR, throwable.getMessage());
    }

    @ExceptionHandler(value = NullPointerException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<String> exceptionHandler(NullPointerException exception) {
        log.error("发生空指针异常！原因是:", exception);
        return Result.of(ExceptionEnum.INTERNAL_SERVER_ERROR, exception.getMessage());
    }

    @ExceptionHandler(value = IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<String> exceptionHandler(IllegalArgumentException exception) {
        log.error("非法参数异常！原因是:", exception);
        return Result.of(ExceptionEnum.PARAMS_NOT_CONVERT, exception.getMessage());
    }

    @ExceptionHandler(value = LockException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<String> lockExceptionHandler(LockException exception) {
        String servletPath = SpringWebUtils.getRequestURI();
        log.error("获取锁失败！servletPath: {}, 原因是: {}", servletPath, exception.getMessage());
        return Result.of(ExceptionEnum.TOO_MANY_REQUESTS, servletPath);
    }

    @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodValidationException.class, HandlerMethodValidationException.class})
    public Result<String> handleValidatedException(Exception exception) {
        if (exception instanceof MethodValidationException ex) {
            return Result.of(ExceptionEnum.BODY_NOT_MATCH, ex.getAllErrors().stream().map(MessageSourceResolvable::getDefaultMessage).collect(Collectors.joining("; ")));
        } else if (exception instanceof HandlerMethodValidationException ex) {
            return Result.of(ExceptionEnum.BODY_NOT_MATCH, ex.getAllErrors().stream().map(MessageSourceResolvable::getDefaultMessage).collect(Collectors.joining("; ")));
        } else if (exception instanceof ConstraintViolationException ex) {
            return Result.of(ExceptionEnum.BODY_NOT_MATCH, ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining("; ")));
        } else if (exception instanceof BindException ex) {
            return Result.of(ExceptionEnum.BODY_NOT_MATCH, ex.getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining("; ")));
        }
        return Result.of(ExceptionEnum.BODY_NOT_MATCH, exception.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Map<String, String>> exception(MethodArgumentNotValidException exception, HttpServletRequest request) {
        Map<String, String> result = new HashMap<>();
        log.error("请求[ {} ] {} 的参数校验发生错误", request.getMethod(), request.getRequestURL());
        for (ObjectError objectError : exception.getBindingResult().getAllErrors()) {
            FieldError fieldError = (FieldError) objectError;
            log.error("参数 {} = {} 校验错误：{}", fieldError.getField(), fieldError.getRejectedValue(), fieldError.getDefaultMessage());
            result.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        return Result.of(ExceptionEnum.BODY_NOT_MATCH, result);
    }
}
